/*
 * Copyright (c) 2020-2099 the original author or authors. All rights reserve.
 */
package org.tianyun.cloud.registrar;

import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.boot.context.properties.source.ConfigurationPropertySource;
import org.springframework.boot.context.properties.source.ConfigurationPropertySources;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.Environment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.util.StringUtils;
import org.tianyun.cloud.configuration.GlobalConfiguration;

import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

/**
 * 自定义bean扫描注册
 *
 * @auther ebert_chan
 */
public abstract class BeanRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, EnvironmentAware {

    private Environment environment;

    private ResourceLoader resourceLoader;

    private BeanDefinitionRegistry registry;

    private ClassPathScanningCandidateComponentProvider scanner;

    /**
     * 注册bean
     *
     * @auther ebert_chan
     */
    public abstract void registerBeanDefinitions();

    /**
     * 类扫描，获取指定注解标识的类
     *
     * @param basePackage
     * @param annotationClass
     * @return
     * @auther ebert_chan
     */
    public Set<BeanDefinition> findCandidateComponents(String basePackage, Class<? extends Annotation> annotationClass) {
        scanner.resetFilters(false);
        AnnotationTypeFilter annotationTypeFilter = new AnnotationTypeFilter(annotationClass);
        scanner.addIncludeFilter(annotationTypeFilter);
        Set<BeanDefinition> candidateComponents = scanner.findCandidateComponents(basePackage);
        return candidateComponents;
    }

    /**
     * 获取BeanDefinitionRegistry
     *
     * @return
     * @auther ebert_chan
     */
    public BeanDefinitionRegistry getBeanDefinitionRegistry() {
        return this.registry;
    }

    /*
     * @see org.springframework.context.EnvironmentAware#setEnvironment(org.springframework.core.env.Environment)
     */
    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    /*
     * @see org.springframework.context.ResourceLoaderAware#setResourceLoader(org.springframework.core.io.ResourceLoader)
     */
    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
    }

    /*
     * @see
     * org.springframework.context.annotation.ImportBeanDefinitionRegistrar#registerBeanDefinitions(org.springframework.core.type.AnnotationMetadata,
     * org.springframework.beans.factory.support.BeanDefinitionRegistry)
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        this.registry = registry;
        scanner = getScanner();
        scanner.setResourceLoader(this.resourceLoader);
        this.registerBeanDefinitions();
    }

    /**
     * 获取主类的包扫描路径
     *
     * @return
     * @auther ebert_chan
     * @see ComponentScan
     */
    public Set<String> getComponentScanningPackages() {
        Set<String> packages = new LinkedHashSet<>();
        packages.add(GlobalConfiguration.SCAN_BASE_PACKAGE);
        String[] names = registry.getBeanDefinitionNames();
        for (String name : names) {
            BeanDefinition definition = registry.getBeanDefinition(name);
            if (definition instanceof AnnotatedBeanDefinition) {
                AnnotatedBeanDefinition annotatedDefinition = (AnnotatedBeanDefinition) definition;
                addComponentScanningPackages(packages, annotatedDefinition.getMetadata());
            }
        }
        return packages;
    }

    /**
     * 类加载
     *
     * @param className
     * @return
     * @auther ebert_chan
     */
    public Class<?> loadClass(String className) {
        try {
            return this.resourceLoader.getClassLoader().loadClass(className);
        } catch (ClassNotFoundException ex) {
            // Swallow and continue
        }
        return null;
    }

    /**
     * 获取注解属性的第一个值
     *
     * @param values
     * @return
     * @auther ebert_chan
     */
    public String getFirstValue(String[] values) {
        if (values != null && values.length > 0) {
            return values[0];
        }
        return null;
    }

    /**
     * 获取注解的name属性值
     *
     * @param attributes
     * @return
     * @auther ebert_chan
     */
    public String getName(Map<String, Object> attributes) {
        String name = (String) attributes.get("name");
        if (!StringUtils.hasText(name)) {
            name = (String) attributes.get("value");
        }
        return name;
    }

    private void addComponentScanningPackages(Set<String> packages, AnnotationMetadata metadata) {
        AnnotationAttributes attributes = AnnotationAttributes.fromMap(metadata.getAnnotationAttributes(ComponentScan.class.getName(), true));
        if (attributes != null) {
            addPackages(packages, attributes.getStringArray("value"));
            addPackages(packages, attributes.getStringArray("basePackages"));
        }
    }

    private void addPackages(Set<String> packages, String[] values) {
        if (values != null) {
            Collections.addAll(packages, values);
        }
    }

    private ClassPathScanningCandidateComponentProvider getScanner() {
        return new ClassPathScanningCandidateComponentProvider(false, this.environment) {

            @Override
            protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
                boolean isCandidate = false;
                if (beanDefinition.getMetadata().isIndependent()) {
                    if (!beanDefinition.getMetadata().isAnnotation()) {
                        isCandidate = true;
                    }
                }
                return isCandidate;
            }
        };
    }

    /**
     * 资源绑定，可以从PropertySources读取配置转化为指定配置类
     *
     * @return
     * @auther ebert_chan
     */
    public Binder getBinder() {
        MutablePropertySources source = ((ConfigurableEnvironment) environment).getPropertySources();
        return new Binder(this.getConfigurationPropertySources(source));
    }

    private Iterable<ConfigurationPropertySource> getConfigurationPropertySources(MutablePropertySources propertySources) {
        return ConfigurationPropertySources.from(propertySources);
    }

    public ResourceLoader getResourceLoader() {
        return resourceLoader;
    }

}
